#include "Params.h"
#include "C0C_Def.h"
#include "Utils.h"

#include <QDebug>
#include <QSettings>
#include <QStringList>
#include <QRegularExpression>


// Bit type enumeration
struct Bit
{
    quint32 bit;

    enum {
        OTHER,
        FLAG,
        PIN,
        PROBABILITY,
        UNSIGNED,
    } type;
};

static const Bit bits[] = {
    {BM_PORTNAME,       Bit::OTHER},
    {BM_REALPORTNAME,   Bit::OTHER},
    {BM_EMUBR,          Bit::FLAG},
    {BM_EMUOVERRUN,     Bit::FLAG},
    {BM_PLUGINMODE,     Bit::FLAG},
    {BM_EXCLUSIVEMODE,  Bit::FLAG},
    {BM_HIDDENMODE,     Bit::FLAG},
    {BM_ALLDATABITS,    Bit::FLAG},
    {BM_PINCTS,         Bit::PIN},
    {BM_PINDSR,         Bit::PIN},
    {BM_PINDCD,         Bit::PIN},
    {BM_PINRI,          Bit::PIN},
    {BM_EMUNOISE,       Bit::PROBABILITY},
    {BM_ADDRTTO,        Bit::UNSIGNED},
    {BM_ADDRITO,        Bit::UNSIGNED},
    };

// Static member initialization
const QMap<quint32, QString> PortParameters::s_bitNames = PortParameters::initBitNames();

QMap<quint32, QString> PortParameters::initBitNames()
{
    QMap<quint32, QString> names;
    names[::BM_PORTNAME] = "PortName";
    names[::BM_REALPORTNAME] = "RealPortName";
    names[::BM_EMUBR] = "EmuBR";
    names[::BM_EMUOVERRUN] = "EmuOverrun";
    names[::BM_PLUGINMODE] = "PlugInMode";
    names[::BM_EXCLUSIVEMODE] = "ExclusiveMode";
    names[::BM_HIDDENMODE] = "HiddenMode";
    names[::BM_ALLDATABITS] = "AllDataBits";
    names[::BM_PINCTS] = "cts";
    names[::BM_PINDSR] = "dsr";
    names[::BM_PINDCD] = "dcd";
    names[::BM_PINRI] = "ri";
    names[::BM_EMUNOISE] = "EmuNoise";
    names[::BM_ADDRTTO] = "AddRTTO";
    names[::BM_ADDRITO] = "AddRITO";
    return names;
}

PortParameters::PortParameters(const QString &service, const QString &physicalPortName, QObject *parent)
    : QObject(parent)
    , m_service(service)
    , m_physicalPortName(physicalPortName)
{
    init();
}

void PortParameters::init()
{
    m_portName.clear();
    m_realPortName.clear();
    m_emuBR = 0;
    m_emuOverrun = 0;
    m_plugInMode = 0;
    m_exclusiveMode = 0;
    m_hiddenMode = 0;
    m_allDataBits = 0;

    m_pinCTS = 0;
    m_pinDSR = 0;
    m_pinDCD = 0;
    m_pinRI = 0;

    m_emuNoise = 0;
    m_addRTTO = 0;
    m_addRITO = 0;

    m_maskChanged = 0;
    m_maskExplicit = 0;
}

bool PortParameters::setPortName(const QString &portName)
{
    if (m_portName.compare(portName, Qt::CaseInsensitive) != 0) {
        if (portName.isEmpty()) {
            qDebug() << "Invalid port name" << portName;
            return false;
        }
        m_portName = portName.toUpper();
        m_maskChanged |= ::BM_PORTNAME;
    }
    return true;
}

bool PortParameters::setRealPortName(const QString &newRealPortName)
{
    if (m_realPortName.compare(newRealPortName, Qt::CaseInsensitive) != 0) {
        if (newRealPortName.isEmpty()) {
            qDebug() << "Invalid real port name" << newRealPortName;
            return false;
        }
        m_realPortName = newRealPortName.toUpper();
        m_maskChanged |= ::BM_REALPORTNAME;
    }
    return true;
}

bool PortParameters::initRealPortName(const QString &realPortName)
{
    if (realPortName.isEmpty()) {
        m_maskExplicit &= ~::BM_REALPORTNAME;
        m_maskChanged &= ~::BM_REALPORTNAME;
        return true;
    }
    m_realPortName = realPortName.toUpper();
    m_maskChanged &= ~::BM_REALPORTNAME;
    m_maskExplicit |= ::BM_REALPORTNAME;
    return true;
}

quint32 *PortParameters::getDwordPointer(quint32 bit)
{
    switch (bit) {
    case ::BM_EMUBR:          return &m_emuBR;
    case ::BM_EMUOVERRUN:     return &m_emuOverrun;
    case ::BM_PLUGINMODE:     return &m_plugInMode;
    case ::BM_EXCLUSIVEMODE:  return &m_exclusiveMode;
    case ::BM_HIDDENMODE:     return &m_hiddenMode;
    case ::BM_ALLDATABITS:    return &m_allDataBits;
    case ::BM_PINCTS:         return &m_pinCTS;
    case ::BM_PINDSR:         return &m_pinDSR;
    case ::BM_PINDCD:         return &m_pinDCD;
    case ::BM_PINRI:          return &m_pinRI;
    case ::BM_EMUNOISE:       return &m_emuNoise;
    case ::BM_ADDRTTO:        return &m_addRTTO;
    case ::BM_ADDRITO:        return &m_addRITO;
    }
    return nullptr;
}

static quint32 getDwordDefault(quint32 bit)
{
    switch (bit) {
    case ::BM_EMUBR:          return C0C_DEFAULT_EMUBR;
    case ::BM_EMUOVERRUN:     return C0C_DEFAULT_EMUOVERRUN;
    case ::BM_PLUGINMODE:     return C0C_DEFAULT_PLUGINMODE;
    case ::BM_EXCLUSIVEMODE:  return C0C_DEFAULT_EXCLUSIVEMODE;
    case ::BM_HIDDENMODE:     return C0C_DEFAULT_HIDDENMODE;
    case ::BM_ALLDATABITS:    return C0C_DEFAULT_ALLDATABITS;
    case ::BM_PINCTS:         return C0C_DEFAULT_PIN_CTS;
    case ::BM_PINDSR:         return C0C_DEFAULT_PIN_DSR;
    case ::BM_PINDCD:         return C0C_DEFAULT_PIN_DCD;
    case ::BM_PINRI:          return C0C_DEFAULT_PIN_RI;
    case ::BM_EMUNOISE:       return C0C_DEFAULT_EMUNOISE;
    case ::BM_ADDRTTO:        return C0C_DEFAULT_ADDRTTO;
    case ::BM_ADDRITO:        return C0C_DEFAULT_ADDRITO;
    }

    return 0;
}

bool PortParameters::setFlag(const QString &newValue, quint32 bit)
{
    quint32 newFlag;

    if (newValue.compare("yes", Qt::CaseInsensitive) == 0) {
        newFlag = 0xFFFFFFFF;
    }
    else if (newValue.compare("no", Qt::CaseInsensitive) == 0) {
        newFlag = 0;
    }
    else {
        qDebug() << "Invalid value" << newValue;
        return false;
    }

    quint32 *pFlag = getDwordPointer(bit);

    if (pFlag == nullptr)
        return false;

    if (*pFlag != newFlag) {
        *pFlag = newFlag;
        m_maskChanged |= bit;
    }

    return true;
}

bool PortParameters::setPin(const QString &newValue, quint32 bit)
{
    quint32 newPin = 0;
    QString value = newValue;

    if (value.startsWith('!')) {
        newPin = C0C_PIN_NEGATIVE;
        value = value.mid(1);
    }

    if (value.compare("rrts", Qt::CaseInsensitive) == 0) {
        newPin |= C0C_PIN_RRTS;
    }
    else if (value.compare("rdtr", Qt::CaseInsensitive) == 0) {
        newPin |= C0C_PIN_RDTR;
    }
    else if (value.compare("rout1", Qt::CaseInsensitive) == 0) {
        newPin |= C0C_PIN_ROUT1;
    }
    else if (value.compare("rout2", Qt::CaseInsensitive) == 0) {
        newPin |= C0C_PIN_ROUT2;
    }
    else if (value.compare("ropen", Qt::CaseInsensitive) == 0) {
        newPin |= C0C_PIN_ROPEN;
    }
    else if (value.compare("lrts", Qt::CaseInsensitive) == 0) {
        newPin |= C0C_PIN_LRTS;
    }
    else if (value.compare("ldtr", Qt::CaseInsensitive) == 0) {
        newPin |= C0C_PIN_LDTR;
    }
    else if (value.compare("lout1", Qt::CaseInsensitive) == 0) {
        newPin |= C0C_PIN_LOUT1;
    }
    else if (value.compare("lout2", Qt::CaseInsensitive) == 0) {
        newPin |= C0C_PIN_LOUT2;
    }
    else if (value.compare("lopen", Qt::CaseInsensitive) == 0) {
        newPin |= C0C_PIN_LOPEN;
    }
    else if (value.compare("on", Qt::CaseInsensitive) == 0) {
        newPin |= C0C_PIN_ON;
    }
    else {
        qDebug() << "Invalid value" << newValue;
        return false;
    }

    quint32 *pPin = getDwordPointer(bit);

    if (pPin == nullptr)
        return false;

    if (*pPin != newPin) {
        *pPin = newPin;
        m_maskChanged |= bit;
    }

    return true;
}

bool PortParameters::setProbability(const QString &newValue, quint32 bit)
{
    quint32 newVal = 0;

    if (newValue.startsWith("0.")) {
        QString fractionalPart = newValue.mid(2);
        
        for (quint32 one = C0C_PROBABILITY_ONE; one > 1; one /= 10) {
            newVal *= 10;

            if (fractionalPart.isEmpty())
                continue;

            QChar ch = fractionalPart.at(0);
            if (!ch.isDigit()) {
                qDebug() << "Invalid value" << newValue;
                return false;
            }

            newVal += ch.digitValue();
            fractionalPart = fractionalPart.mid(1);
        }

        // Remove trailing zeros
        while (!fractionalPart.isEmpty() && fractionalPart.at(0) == '0') {
            fractionalPart = fractionalPart.mid(1);
        }

        if (!fractionalPart.isEmpty()) {
            qDebug() << "Too long value" << newValue;
            return false;
        }
    }
    else if (newValue == "0") {
        // newVal is already 0
    }
    else {
        qDebug() << "Invalid value" << newValue;
        return false;
    }

    quint32 *pVal = getDwordPointer(bit);

    if (pVal == nullptr)
        return false;

    if (*pVal != newVal) {
        *pVal = newVal;
        m_maskChanged |= bit;
    }

    return true;
}

bool PortParameters::setUnsigned(const QString &newValue, quint32 bit)
{
    bool ok;
    quint32 newVal = newValue.toUInt(&ok);

    if (!ok) {
        qDebug() << "Invalid value" << newValue;
        return false;
    }

    quint32 *pVal = getDwordPointer(bit);

    if (pVal == nullptr)
        return false;

    if (*pVal != newVal) {
        *pVal = newVal;
        m_maskChanged |= bit;
    }

    return true;
}

bool PortParameters::setBit(const QString &value, const Bit &bit)
{
    if (value == "*")
        return true;

    if (value == "-") {
        if (m_maskExplicit & bit.bit) {
            m_maskExplicit &= ~bit.bit;
            m_maskChanged |= bit.bit;
        }
        return true;
    }

    bool result = false;
    switch (bit.type) {
    case Bit::FLAG:
        result = setFlag(value, bit.bit);
        break;
    case Bit::PIN:
        result = setPin(value, bit.bit);
        break;
    case Bit::PROBABILITY:
        result = setProbability(value, bit.bit);
        break;
    case Bit::UNSIGNED:
        result = setUnsigned(value, bit.bit);
        break;
    case Bit::OTHER:
        if (bit.bit == BM_PORTNAME) {
            setPortName(value);
            result = true;
        }
        else if (bit.bit == BM_REALPORTNAME) {
            result = setRealPortName(value);
        }
        break;
    }

    if (result && (m_maskChanged & bit.bit)) {
        m_maskExplicit |= bit.bit;
    }

    return result;
}

QString PortParameters::getParametersKey() const
{
    return QString("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\%1\\Parameters\\%2")
    .arg(m_service, m_physicalPortName);
}

void PortParameters::loadDword(QSettings &settings, quint32 bit)
{
    quint32 *pDw = getDwordPointer(bit);
    QString name = s_bitNames.value(bit);

    if (pDw == nullptr || name.isEmpty())
        return;

    if (settings.contains(name)) {
        *pDw = settings.value(name).toUInt();
        m_maskExplicit |= bit;
    }
}

bool PortParameters::saveDword(QSettings &settings, quint32 bit)
{
    if (m_maskChanged & bit) {
        const quint32 *pDw = getDwordPointer(bit);
        QString name = s_bitNames.value(bit);

        if (pDw == nullptr || name.isEmpty())
            return false;

        if (m_maskExplicit & bit) {
            settings.setValue(name, *pDw);
        } else {
            settings.remove(name);
        }

        m_maskChanged &= ~bit;
    }

    return true;
}

bool PortParameters::load()
{
    init();

    QString keyPath = getParametersKey();
    QSettings settings(keyPath, QSettings::NativeFormat);

    // Load port name
    if (settings.contains("PortName")) {
        m_portName = settings.value("PortName").toString();
        m_maskExplicit |= ::BM_PORTNAME;
    }

    // Load all DWORD values
    for (const Bit &bit : bits) {
        if (getDwordPointer(bit.bit)) {
            loadDword(settings, bit.bit);
        }
    }

    return true;
}

bool PortParameters::save()
{
    if (!hasChanged()) {
        return true;
    }

    QString keyPath = getParametersKey();
    QSettings settings(keyPath, QSettings::NativeFormat);

    // Save port name
    if (m_maskChanged & ::BM_PORTNAME) {
        if (m_maskExplicit & ::BM_PORTNAME) {
            settings.setValue("PortName", m_portName);
        } else {
            settings.remove("PortName");
        }
        m_maskChanged &= ~::BM_PORTNAME;
    }

    // Save all DWORD values
    for (const Bit &bit : bits) {
        if (getDwordPointer(bit.bit)) {
            if (!saveDword(settings, bit.bit)) {
                return false;
            }
        }
    }

    emit parametersChanged();
    return true;
}

bool PortParameters::parseParametersString(const QString &parameters)
{
    // 备份当前状态
    quint32 backup_maskChanged = m_maskChanged;
    quint32 backup_maskExplicit = m_maskExplicit;
    QString backup_portName = m_portName;
    QString backup_realPortName = m_realPortName;
    quint32 backup_emuBR = m_emuBR;
    quint32 backup_emuOverrun = m_emuOverrun;
    quint32 backup_plugInMode = m_plugInMode;
    quint32 backup_exclusiveMode = m_exclusiveMode;
    quint32 backup_hiddenMode = m_hiddenMode;
    quint32 backup_allDataBits = m_allDataBits;
    quint32 backup_pinCTS = m_pinCTS;
    quint32 backup_pinDSR = m_pinDSR;
    quint32 backup_pinDCD = m_pinDCD;
    quint32 backup_pinRI = m_pinRI;
    quint32 backup_emuNoise = m_emuNoise;
    quint32 backup_addRTTO = m_addRTTO;
    quint32 backup_addRITO = m_addRITO;

    // 回滚闭包，减少重复代码
    auto rollback = [&]() {
        m_maskChanged = backup_maskChanged;
        m_maskExplicit = backup_maskExplicit;
        m_portName = backup_portName;
        m_realPortName = backup_realPortName;
        m_emuBR = backup_emuBR;
        m_emuOverrun = backup_emuOverrun;
        m_plugInMode = backup_plugInMode;
        m_exclusiveMode = backup_exclusiveMode;
        m_hiddenMode = backup_hiddenMode;
        m_allDataBits = backup_allDataBits;
        m_pinCTS = backup_pinCTS;
        m_pinDSR = backup_pinDSR;
        m_pinDCD = backup_pinDCD;
        m_pinRI = backup_pinRI;
        m_emuNoise = backup_emuNoise;
        m_addRTTO = backup_addRTTO;
        m_addRITO = backup_addRITO;
    };

    if (parameters == "-" || parameters == "*") {
        for (const Bit &bit : bits) {
            if (!setBit(parameters, bit)) {
                rollback();
                return false;
            }
        }
    } else {
        QStringList paramList = parameters.split(',', QString::SkipEmptyParts);

        for (const QString &param : paramList) {
            QStringList keyValue = param.split('=');
            if (keyValue.size() != 2) {
                qDebug() << "Missing value for" << param;
                rollback();
                return false;
            }

            QString key = keyValue[0].trimmed();
            QString value = keyValue[1].trimmed();

            bool found = false;
            for (const Bit &bit : bits) {
                QString bitName = s_bitNames.value(bit.bit);
                if (!bitName.isEmpty() && key.compare(bitName, Qt::CaseInsensitive) == 0) {
                    if (!setBit(value, bit)) {
                        rollback();
                        return false;
                    }
                    found = true;
                    break;
                }
            }

            if (!found) {
                qDebug() << "Invalid parameter" << key;
                rollback();
                return false;
            }
        }
    }

    return true;
}

QString PortParameters::getParametersString(bool detail)
{
    QString result = QString("PortName=%1")
    .arg((m_maskExplicit & ::BM_PORTNAME) ? m_portName :
             (detail ? m_physicalPortName : "-"));

    if (m_maskExplicit & ::BM_REALPORTNAME) {
        result += QString(",RealPortName=%1").arg(m_realPortName);
    }

    for (const Bit &bit : bits) {
        quint32 bitValue = bit.bit;
        if (!getDwordPointer(bitValue))
            continue;

        QString bitName = s_bitNames.value(bitValue);
        if (bitName.isEmpty())
            continue;

        quint32 value;
        // 显式设定则默认显示
        if (m_maskExplicit & bitValue) {
            value = *getDwordPointer(bitValue);
        } else if (detail) {
            // 非显式并启用详细输出则使用默认值
            value = getDwordDefault(bitValue);
        } else {
            continue;
        }

        QString valueStr;
        switch (bit.type) {
        case Bit::FLAG:
            valueStr = (value != 0) ? "yes" : "no";
            break;
        case Bit::PIN:
            // Pin value formatting would need more complex logic
            valueStr = QString::number(value);
            break;
        case Bit::PROBABILITY:
            if (value == 0) {
                valueStr = "0";
            } else {
                valueStr = QString("0.%1").arg(value, 0, 10);
            }
            break;
        case Bit::UNSIGNED:
            valueStr = QString::number(value);
            break;
        default:
            continue;
        }

        result += QString(",%1=%2").arg(bitName, valueStr);
    }

    return result;
}

QString PortParameters::getPortName() const
{
    return (m_maskExplicit & ::BM_PORTNAME) ? m_portName : m_physicalPortName;
}

QString PortParameters::getRealPortName() const
{
    return (m_maskExplicit & ::BM_REALPORTNAME) ? m_realPortName : QString();
}

QString PortParameters::getHelp()
{
    return QString(
        "Port Parameters Help:\n"
        "PortName=<name>     - Set port name\n"
        "RealPortName=<name> - Set real port name\n"
        "EmuBR=yes|no        - Enable/disable baud rate emulation\n"
        "EmuOverrun=yes|no   - Enable/disable overrun emulation\n"
        "PlugInMode=yes|no   - Enable/disable plug-in mode\n"
        "ExclusiveMode=yes|no - Enable/disable exclusive mode\n"
        "HiddenMode=yes|no   - Enable/disable hidden mode\n"
        "AllDataBits=yes|no  - Enable/disable all data bits\n"
        "cts=<pin>           - Set CTS pin\n"
        "dsr=<pin>           - Set DSR pin\n"
        "dcd=<pin>           - Set DCD pin\n"
        "ri=<pin>            - Set RI pin\n"
        "EmuNoise=<prob>     - Set noise emulation probability\n"
        "AddRTTO=<ms>        - Add read total timeout\n"
        "AddRITO=<ms>        - Add read interval timeout\n"
        );
}
